package annotation;

import static java.lang.System.out;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface TypeAnnotation {
	String name();
	int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MethodAnnotation {
	String name();
	int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface ParamAnnotation {
	String name();
	int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface FieldAnnotation {
	String name();
	int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Field2Annotation {
	String name();
	int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD})
@interface AllAnnotation {
	String id();
	String pw();
}

@TypeAnnotation(name="class",value=1)
class MyClass{
	@MethodAnnotation(name="method",value=2)	
	public void process(){}
	
	public void processParam(
			@ParamAnnotation(name="parameter1",value=31) String param,
			@ParamAnnotation(name="parameter2",value=32) Integer num){}
	
	@FieldAnnotation(name="field",value=41)
	@Field2Annotation(name="field2",value=42)	
	public String memberField;
}

@AllAnnotation(id="like",pw="sky")
class AllClass{
	@AllAnnotation(id="like",pw="sky")
	public int id;
	
	@AllAnnotation(id="like",pw="sky")
	public void process(@AllAnnotation(id="like",pw="sky") int id){}
}



// key 이름이 value 일때는 사용 시 생략할 수 있다.
// key를 두개 이상 지정할 때는 value를 써야 한다.
// value key의 값을 꼭 지정해야 하며, default를 이용할 수 있다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ValueAnnotation {
	String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ValueAnnotation2 {
	String value() default "";
}

@ValueAnnotation("hello")
@ValueAnnotation2()
// @ValueAnnotation() //error
class ExtraClass{
	
}

public class AnnotationTest {
	public static void main(String[] args){
		out.println("[Type]");
		{
			Annotation[] annotations = MyClass.class.getAnnotations();
			for(Annotation annotation:annotations){
				TypeAnnotation ann = (TypeAnnotation)annotation;
				out.println(ann.name()+","+ann.value());
			}
		}
		
		
		out.println("[Method]");
		try{
			Method method = MyClass.class.getMethod("process", new Class[]{});
			Annotation[] annotations = method.getDeclaredAnnotations();
			for(Annotation annotation:annotations){
				MethodAnnotation ann = (MethodAnnotation)annotation;
				out.println(ann.name()+","+ann.value());
			}
		}catch(NoSuchMethodException e){}
		
				
		out.println("[Parameter]");
		try{
			Method method = MyClass.class.getMethod("processParam", String.class,Integer.class);
			Annotation[][] paramAnnotations = method.getParameterAnnotations();			
			Class[] paramTypes = method.getParameterTypes();
			
			int i=0;
			Class paramType = null;
			for(Annotation[] annotations:paramAnnotations){
				paramType = paramTypes[i++];
				for(Annotation annotation:annotations){
					ParamAnnotation param = (ParamAnnotation)annotation;
					out.println(paramType.getName()+","+param.name()+","+param.value());
				}
			}
		}catch(NoSuchMethodException e){}
		
		
		out.println("[Field]");
		try{
			Field field = MyClass.class.getField("memberField");
			FieldAnnotation annotation = field.getAnnotation(FieldAnnotation.class);
			out.println(annotation.name()+","+annotation.value());
			Field2Annotation annotation2 = field.getAnnotation(Field2Annotation.class);
			out.println(annotation2.name()+","+annotation2.value());
		}catch(NoSuchFieldException e){}
	}
}


